#include <dm/os/db/db_pg.hpp>
#include <dm/string/string.hpp>
#include <dm/os/db/resultset_pg.hpp>
#include <dm/os/log/logger.hpp>

namespace dm{
namespace os{

static const char* logModule = "CDbPg.os.dm";

CDbPg::CDbPg( const char* dbName,const char* host,short port,const EExtention& extention ):CDb(dbName,host,port),m_extention(extention){
	m_con = NULL;
}

CDbPg::~CDbPg(){
	if( m_con!=NULL )
		PQfinish(m_con);
}

CDb::EDbType CDbPg::dbType(){
	if( m_extention==ExHgdb )
		return DtHgdb;

	return DtPg;
}

bool CDbPg::isConnected(){
	if( m_con!=NULL ){
		switch(PQstatus(m_con)){
		case CONNECTION_OK:
			return true;

		case CONNECTION_STARTED:
			log().info(THISMODULE "等待进行连接");
			break;
		case CONNECTION_MADE:
			log().info(THISMODULE "连接成功，等待发送");
			break;
		case CONNECTION_AWAITING_RESPONSE:
			log().info(THISMODULE "等待来自服务器的响应");
			break;
		case CONNECTION_AUTH_OK:
			log().info(THISMODULE "已收到认证；等待后端启动结束");
			break;
		case CONNECTION_SSL_STARTUP:
			log().info(THISMODULE "协商SSL加密");
			break;
		case CONNECTION_SETENV:
			log().info(THISMODULE "协商环境驱动的参数设置");
			break;
		case CONNECTION_BAD:
			log().info(THISMODULE "链接故障");
			break;
		default:
			log().info(THISMODULE "未知原因");
		}
	}

	return false;
}

bool CDbPg::connect( const char* user,const char* pw ){
    if( m_con ){
    	log().debug(THISMODULE "释放连接");
        PQfinish(m_con);
    }

    if( m_port==-1 )
    	m_con = PQsetdbLogin(m_host.c_str(),NULL,NULL,NULL,m_dbName.c_str(),user,pw);
    else{
    	dm::string::CString port = dm::string::CString::fromInt16(m_port);
    	log().debug(THISMODULE "连接数据库:%s@%s:%s",m_dbName.c_str(),m_host.c_str(),port.c_str());
    	m_con = PQsetdbLogin(m_host.c_str(),port.c_str(),NULL,NULL,m_dbName.c_str(),user,pw);
    	if( m_con )
    		PQerrorMessage(m_con);
    	else{
    		log().error(THISMODULE "连接失败:%s:%s",m_host.c_str(),m_port);
    	}
    }
	return isConnected();
}

void CDbPg::disconnect( ){
    if( m_con ){
        PQfinish(m_con);
        m_con = NULL;
    }
}

bool CDbPg::exec( const char* sql ){
    if( isConnected() ){
    	if( PQsendQuery(m_con, sql)==1 ){
    		PGresult* result = PQgetResult(m_con);
    		while( result==NULL ){
            	if( PQconsumeInput(m_con)==0 ){
            		if( PQisBusy(m_con)==0 ){
            			// 异常
            			PGcancel* cancel = PQgetCancel(m_con);

            			char errbuf[256];
            			if( PQcancel(cancel, errbuf, 256)==0 ){
            				log().error(THISMODULE "取消执行失败:%s",errbuf);
            			}
            		}
            	}
            	result = PQgetResult(m_con);
    		}

            if( result ){
                if( PQresultStatus(result)==PGRES_COMMAND_OK ){
                    PQclear(result);
                    return true;
                }
                PQclear(result);
            }
    	}
    }    

	return false;
}

dm::int64 CDbPg::execWithLastId( const char* sql ){
	dm::int64 rt = -1;
    if( isConnected() ){
    	int len = strlen(sql);
    	char* sql_x = new char[len+20] ;
    	std::sprintf(sql_x,"%s RETURNING ID",sql);
    	if( PQsendQuery(m_con, sql_x)==1 ){
    		PGresult* result = PQgetResult(m_con);
    		while( result==NULL ){
    			if( PQconsumeInput(m_con)==0 ){
    				if( PQisBusy(m_con)==0 ){
    					PGcancel* cancel = PQgetCancel(m_con);
    					char errbuf[256];
    					if( PQcancel(cancel, errbuf, 256)==0 ){
    						log().error(THISMODULE "取消查询失败:%s",errbuf);
    					}
    				}
    			}

    			result = PQgetResult(m_con);
    		}

            if( result ){
                if( PQresultStatus(result)==PGRES_TUPLES_OK ){
                	rt = atol(PQgetvalue(result, 0, 0));
                    PQclear(result);
                }
                PQclear(result);
            }
    	}

        delete [] sql_x;
    }

	return rt;
}

CResultSet* CDbPg::query( const char* sql ){
	if( isConnected() ){
        CResultSetPg* rs = new CResultSetPg();

        if( PQsendQuery(m_con, sql)==1 ){
            do{
            	if( PQconsumeInput(m_con)==0 ){
            		if( PQisBusy(m_con)==0 ){
            			// 异常
            			PGcancel* cancel = PQgetCancel(m_con);

            			char errbuf[256];
            			if( PQcancel(cancel, errbuf, 256)==0 ){
            				log().error(THISMODULE "取消查询失败:%s",errbuf);
            			}
            		}
            	}

            	rs->m_rst = PQgetResult(m_con);
            }while( rs->m_rst==NULL );

            if( rs->m_rst ){
                rs->m_r = -1;
                if( PQresultStatus(rs->m_rst)==PGRES_TUPLES_OK )
                    return rs;
            }
        }else{
        	log().debug(THISMODULE "发送查询失败");
        }

        delete rs;
	}

	return NULL;
}

/**
 * 获取匹配字符串表名
 * @param pattern
 * @param rs
 * @return
 */
CResultSet* CDbPg::getTables( const char* pattern ){
	std::string sql = "select table_name from information_schema.tables where table_type='BASE TABLE' and table_name like '";
	sql += pattern;
	sql += "'";

	return query(sql.c_str());
}

}
}
